@sayknow-cli/coding-agent 0.5.1 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +185 -14
  2. package/dist/types/cli/update-cli.d.ts +8 -1
  3. package/dist/types/commands/session.d.ts +7 -0
  4. package/dist/types/config/atomic-yaml-patch.d.ts +20 -1
  5. package/dist/types/config/keybindings.d.ts +10 -0
  6. package/dist/types/config/model-registry.d.ts +1 -1
  7. package/dist/types/config/models-config-schema.d.ts +4 -0
  8. package/dist/types/config/settings-schema.d.ts +9 -0
  9. package/dist/types/config/telegram-autostart.d.ts +9 -1
  10. package/dist/types/dap/client.d.ts +1 -0
  11. package/dist/types/lsp/client.d.ts +5 -0
  12. package/dist/types/modes/components/pet-capability.d.ts +8 -7
  13. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  14. package/dist/types/modes/components/sayknow-pet-widget.d.ts +1 -1
  15. package/dist/types/modes/components/welcome.d.ts +1 -0
  16. package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
  17. package/dist/types/modes/interactive-mode.d.ts +13 -1
  18. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +7 -0
  19. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -0
  20. package/dist/types/modes/types.d.ts +7 -1
  21. package/dist/types/runtime-mcp/transports/stdio.d.ts +2 -0
  22. package/dist/types/session/agent-session.d.ts +9 -0
  23. package/dist/types/session/internal/managed-session-scope.d.ts +3 -1
  24. package/dist/types/session/internal/managed-session-storage.d.ts +2 -0
  25. package/dist/types/session/session-manager.d.ts +9 -0
  26. package/dist/types/session/session-storage.d.ts +17 -0
  27. package/dist/types/session-import/claude.d.ts +26 -0
  28. package/dist/types/session-import/codex.d.ts +22 -0
  29. package/dist/types/session-import/command.d.ts +20 -0
  30. package/dist/types/session-import/detect.d.ts +22 -0
  31. package/dist/types/session-import/index.d.ts +7 -0
  32. package/dist/types/session-import/redact.d.ts +23 -0
  33. package/dist/types/session-import/service.d.ts +28 -0
  34. package/dist/types/session-import/types.d.ts +125 -0
  35. package/dist/types/skc-runtime/boot-generation.d.ts +59 -0
  36. package/dist/types/skc-runtime/launch-tmux.d.ts +10 -2
  37. package/dist/types/skc-runtime/launch-worktree.d.ts +17 -1
  38. package/dist/types/skc-runtime/session-restore-runtime.d.ts +41 -0
  39. package/dist/types/skc-runtime/session-restore.d.ts +99 -0
  40. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +160 -0
  41. package/dist/types/skc-runtime/tmux-sessions.d.ts +26 -1
  42. package/dist/types/slash-commands/builtin-registry.d.ts +4 -0
  43. package/dist/types/slash-commands/types.d.ts +5 -0
  44. package/dist/types/tools/ask.d.ts +169 -4
  45. package/package.json +16 -7
  46. package/scripts/generate-sdk-operation-inventory.ts +5 -6
  47. package/src/cli/config-cli.ts +23 -8
  48. package/src/cli/update-cli.ts +265 -21
  49. package/src/commands/session.ts +88 -2
  50. package/src/config/atomic-yaml-patch.ts +167 -30
  51. package/src/config/keybindings.ts +10 -0
  52. package/src/config/model-profiles.ts +69 -1
  53. package/src/config/model-registry.ts +72 -38
  54. package/src/config/models-config-schema.ts +1 -1
  55. package/src/config/settings-schema.ts +9 -0
  56. package/src/config/settings.ts +13 -2
  57. package/src/config/telegram-autostart.ts +11 -4
  58. package/src/dap/client.ts +78 -30
  59. package/src/defaults/skc/skills/deep-interview/SKILL.md +29 -3
  60. package/src/internal-urls/docs-index.generated.ts +5 -4
  61. package/src/lsp/client.ts +17 -29
  62. package/src/main.ts +1 -1
  63. package/src/modes/action-registry.ts +2 -0
  64. package/src/modes/components/pet-capability.ts +22 -13
  65. package/src/modes/components/pet-selector.ts +1 -1
  66. package/src/modes/components/sayknow-pet-widget.ts +41 -7
  67. package/src/modes/components/welcome.ts +5 -0
  68. package/src/modes/controllers/event-controller.ts +1 -1
  69. package/src/modes/controllers/goal-mode-controller.ts +7 -2
  70. package/src/modes/controllers/input-controller.ts +71 -6
  71. package/src/modes/controllers/plan-mode-controller.ts +8 -1
  72. package/src/modes/controllers/runtime-mcp-command-controller.ts +14 -0
  73. package/src/modes/controllers/selector-controller.ts +47 -19
  74. package/src/modes/interactive-mode.ts +54 -4
  75. package/src/modes/shared/agent-wire/unattended-session.ts +40 -9
  76. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -0
  77. package/src/modes/types.ts +5 -1
  78. package/src/notifications/lifecycle-control-runtime.ts +258 -179
  79. package/src/prompts/system/eager-todo.md +2 -0
  80. package/src/prompts/system/plan-mode-approved.md +1 -1
  81. package/src/prompts/system/system-prompt.md +4 -2
  82. package/src/runtime-mcp/transports/stdio.ts +96 -33
  83. package/src/sdk/broker/lifecycle.ts +6 -5
  84. package/src/sdk/bus/lifecycle-control-runtime.ts +189 -110
  85. package/src/sdk/protocol/operation-inventory.generated.json +33 -0
  86. package/src/sdk/session.ts +25 -18
  87. package/src/session/agent-session.ts +120 -25
  88. package/src/session/internal/managed-session-scope.ts +41 -6
  89. package/src/session/internal/managed-session-storage.ts +14 -0
  90. package/src/session/session-manager.ts +113 -11
  91. package/src/session/session-storage.ts +70 -0
  92. package/src/session-import/claude.ts +382 -0
  93. package/src/session-import/codex.ts +458 -0
  94. package/src/session-import/command.ts +61 -0
  95. package/src/session-import/detect.ts +133 -0
  96. package/src/session-import/index.ts +27 -0
  97. package/src/session-import/redact.ts +125 -0
  98. package/src/session-import/service.ts +749 -0
  99. package/src/session-import/types.ts +163 -0
  100. package/src/skc-runtime/boot-generation.ts +172 -0
  101. package/src/skc-runtime/launch-tmux.ts +219 -41
  102. package/src/skc-runtime/launch-worktree.ts +277 -4
  103. package/src/skc-runtime/session-restore-runtime.ts +120 -0
  104. package/src/skc-runtime/session-restore.ts +296 -0
  105. package/src/skc-runtime/session-state-sidecar.ts +41 -0
  106. package/src/skc-runtime/tmux-owner-isolation.ts +665 -0
  107. package/src/skc-runtime/tmux-sessions.ts +284 -108
  108. package/src/slash-commands/acp-builtins.ts +5 -1
  109. package/src/slash-commands/builtin-registry.ts +109 -5
  110. package/src/slash-commands/types.ts +5 -0
  111. package/src/tools/ask.ts +188 -10
  112. package/src/tools/eval.ts +2 -2
  113. package/src/export/html/template.css +0 -1060
  114. package/src/export/html/template.html +0 -47
  115. package/src/export/html/template.js +0 -2348
  116. package/vendor/insane-search/engine/tests/test_hardening.py +0 -57
  117. package/vendor/insane-search/engine/tests/test_smoke.py +0 -152
  118. package/vendor/insane-search/engine/tests/test_u1.py +0 -200
  119. package/vendor/insane-search/engine/tests/test_u4.py +0 -131
  120. package/vendor/insane-search/engine/tests/test_u5.py +0 -163
  121. package/vendor/insane-search/engine/tests/test_u7.py +0 -124
  122. package/vendor/insane-search/engine/tests/test_u8.py +0 -216
package/CHANGELOG.md CHANGED
@@ -1,13 +1,184 @@
1
1
  # Changelog
2
2
 
3
- Sayknow-CLI is a rebranded fork of [gajae-code](https://github.com/Yeachan-Heo/gajae-code).
3
+ ## [Unreleased]
4
+
5
+ ## [0.5.6] - 2026-08-28
6
+
7
+ ## [0.5.4] - 2026-08-28
8
+
9
+ Sayknow-CLI descends from an upstream MIT agent harness; see [`NOTICE.md`](../../NOTICE.md).
4
10
  This file tracks the **fork's own releases**; upstream's full feature history lives
5
11
  in that project. Each release notes the upstream version it is built on.
6
12
 
7
13
 
14
+ ## [0.5.3] - 2026-08-28
15
+
16
+ ### Added
17
+
18
+ - Added zero-configuration oMLX and loopback-safe SGLang discovery through the shared OpenAI-compatible registry, including reviewed oMLX model profiles, thinking-effort preservation, schemas, and local-provider tests.
19
+ - Added explicit-file `/import-session` support for Codex rollout JSONL, Claude Code JSONL, and claude.ai exports with bounded redacted context, quarantined unmappable records, durable provenance, workspace-scoped idempotent native publication, and ACP isolation for path-bearing import, export, and move commands.
20
+ ### Fixed
21
+
22
+ - Imported-session targets activate only through an idle-only session transition; a response or turn that wins the activation race leaves the reused or newly published target intact for later resume instead of interrupting active work.
23
+
24
+ - LSP, DAP, and stdio MCP protocol writes now serialize complete write/flush transactions, await sink backpressure, propagate peer-close failures, and terminalize stale transports on fatal writes or stdout EOF instead of leaving delayed unhandled EPIPE errors or timeout-bound pending requests.
25
+ - Managed sessions now sample transcript size proactively only through a retained exact-file authority, treat unavailable sampling as unknown, and recover an authority-bound `content_too_large` append by atomically rewriting the exact live entry set; failed rewrites latch persistence and roll back volatile branch mutation before later appends.
26
+ - Explicit empty tool selections such as `--no-tools` now remain authoritative across goal/workflow restoration, session branching, and plan-mode transitions instead of silently reactivating built-in tools.
27
+ - `config set` and `config reset` now report success only after durable persistence; atomic YAML writes retain their original resolved target and fail closed on symlink or target-identity retargeting.
28
+ - `skc --worktree` now installs Bun, npm, or pnpm dependencies from the worktree's own matching lockfile instead of linking a package checkout to the source tree's `node_modules`; nested package roots keep their cwd, legacy owned links are migrated, unrelated links are refused, and repeated launches reuse the local install location.
29
+ - Release publication now emits Sayknow-owned SHA-256 manifests, and standalone installers plus `skc update` verify the selected platform binary before replacing an installation; missing or mismatched integrity metadata and concurrent installers fail closed.
30
+
31
+ ### Changed
32
+
33
+ - The command palette now exposes availability-gated follow-up, queue editing, immediate-send, and transcript-turn actions; their default keybindings remain unchanged, while user remaps dispatch through the shared action registry.
34
+ - Korean and emoji escapes in `ask` question text and option labels now recover as display-only content while ids and workflow metadata remain fail-closed.
35
+ - The todo HUD can be expanded from its action/keybinding, startup can skip the logo sweep, and provider or MCP OAuth URLs copy only through an explicit remappable action.
36
+ - **Upstream independence.** Retired the upstream sync/regeneration pipeline
37
+ (`rebrand/` layer plus the generation scripts); this tree is now the single
38
+ source of truth and upstream fixes are ported by cherry-pick only. See
39
+ `docs/FORK_MAINTENANCE.md`.
40
+ - `bun run release` now enforces the fork policy end to end: runs from the
41
+ `sayknow-fork` branch, tags `sayknow-vX.Y.Z`, checks monotonicity against the
42
+ fork tag series only, refuses same-version plain `v` tags on origin, and the
43
+ release attaches both release-evidence JSONs for the daily public-sync live
44
+ check.
45
+ - Scrubbed upstream brand references from public docs, the embedded docs
46
+ index, and test fixtures (also fixing two fixture assertions the original
47
+ rename had broken). MIT attribution remains in `LICENSE` and `NOTICE.md`.
48
+
49
+ ## [0.5.2] — 2026-08-14
50
+
51
+ Built on upstream **v0.12.0**.
52
+
53
+ ### Fixed (unattended workflow gates were unanswerable through the control plane)
54
+
55
+ `UnattendedSessionControlPlane.emitGate` opened gates without a broker
56
+ continuation, so every gate it emitted was quarantined as
57
+ `opened_without_continuation` and could never be answered; its `advance` hook
58
+ also settled the waiter early, violating the broker's post-advance liveness
59
+ check, and accepted gates then failed terminalization for lack of a proof. The
60
+ control plane now registers a live continuation, settles waiters in
61
+ `completeAccepted` (mirroring the session-side emitter), and declares the
62
+ designed `not_published` terminal proof. The ask tool regained the fork's gate
63
+ semantics on top of that: a negotiated unattended emitter wins over an attended
64
+ UI, clarification answers surface the user's question instead of aborting as a
65
+ cancel, multi-select keeps its empty-"Next" semantics through the gate schema,
66
+ and gate metadata carries the Next/Done navigation label again.
67
+
68
+ ### Fixed (ask wire schema: deep-interview intent branches and gate addressing)
69
+
70
+ The deep-interview intent contract is enforced **on the wire** again: ask's
71
+ `deepInterview` metadata is a union of three mutually exclusive strict branches
72
+ (ordinary round / Round-0 `intent_contract` locked to `component:
73
+ "review-topology"` / post-Round-0 `intent_review` with `round >= 1`), so a
74
+ provider that only sees the JSON schema cannot combine a manifest lock with a
75
+ reduction review or attach either to the wrong round. Questions also regained
76
+ the `workflowGate` stage/kind override for addressing non-deep-interview gates,
77
+ and such overridden questions are excluded from the interview recorder.
78
+
79
+ ### Fixed (`SKC_PY` / `PI_PY` / `PI_JS` eval backend selection was ignored)
80
+
81
+ `resolveEvalBackends` existed but the eval tool never called it: backend
82
+ allowance was read from settings only, so `SKC_PY=js` still spawned the Python
83
+ kernel. The tool now resolves allowance through the documented precedence
84
+ (`SKC_PY`, then legacy `PI_PY`/`PI_JS`, then `eval.py`/`eval.js` settings).
85
+
86
+ ### Fixed (stale model discovery result could overwrite a fresher one)
87
+
88
+ Overlapping `refreshProvider` calls for the same provider raced without any
89
+ ordering guard: whichever fetch *completed* last published its discovery state
90
+ and model list, so a slow stale response could erase the models a fresher
91
+ refresh had just delivered. Discovery now carries a monotonic per-provider
92
+ sequence and only the newest-started refresh may publish state or contribute
93
+ models.
94
+
95
+ ### Fixed (post-merge repair: type-check and test contracts realigned)
96
+
97
+ The upstream merges left the workspace `check` red for several releases: test
98
+ files frozen at fork v0.4.7 kept exercising APIs their source had since dropped
99
+ (session-sticky canonical model resolution, `getSelectorSuppressionStatus`,
100
+ `refreshPresetProfiles`, the pre-broker in-process `AcpAgent`, the fork-era
101
+ bridge-client `WorkflowGate` surface). Stale suites superseded by current
102
+ coverage were removed, the survivors were realigned to the shipped APIs, the
103
+ form-elicitation bridge kept its only coverage via a ported focused suite, the
104
+ `node-pty` dev dependency the merge dropped from `@sayknow-cli/tui` is restored,
105
+ and the extracted model helper modules (`config/model-auth`,
106
+ `config/model-bindings-applier`, `config/model-discovery-manager`) are now
107
+ explicitly unexported, matching the documented package surface.
108
+
109
+ ### Fixed (todo_write truncation and retry loops)
110
+
111
+ Structurally complete JSON tool calls now execute even when a Responses provider
112
+ hits its output-token limit before emitting the final item event. Invalid todo
113
+ payloads retry at most once, while transport failures and repeated errors fail
114
+ open so the requested work continues without visible todo tracking.
115
+
116
+ ### Fixed (v0.5.0 upstream merge reverted the fork's tmux graphics support)
117
+
118
+ The v0.12.0 upstream merge overwrote `packages/tui/src/tui.ts` and silently
119
+ dropped three fork-only behaviors, which is why the pet was unavailable in every
120
+ tmux session and `packages/tui/test/sixel-probe.test.ts` shipped with three
121
+ failing tests:
122
+
123
+ - the capability probe refused to run under any multiplexer, so
124
+ `isSixelMultiplexerEnabled()` became dead code
125
+ - the sixel probe was no longer wrapped in tmux's DCS passthrough envelope, so
126
+ tmux answered for a client it knows nothing about
127
+ - the `CSI 16 t` cell-size query lost its passthrough wrapper (the v0.4.6 fix),
128
+ so tmux reported a cell size the outer terminal never uses
129
+
130
+ All three are restored, with the probe deadline back at 600 ms under tmux to
131
+ cover the passthrough round trip.
132
+
133
+ ### Fixed (transcript images stacked over the text under tmux)
134
+
135
+ Enabling the sixel probe under tmux also switched INLINE graphics on, and the
136
+ inline path wrapped every raster in the DCS passthrough envelope. Passthrough
137
+ writes pixels straight into the OUTER terminal's image plane at its *physical*
138
+ cursor: tmux neither positions that cursor for the pane nor records the pixels,
139
+ so tool-result screenshots landed in the wrong row and survived every repaint —
140
+ each render stacked another copy over the transcript.
141
+
142
+ Inline placements now follow the same ownership rule the pet already used:
143
+
144
+ - tmux advertising the `sixel` terminal-feature (which the SKC tmux profile sets
145
+ automatically) parses the raster into its own screen model, so the raster is
146
+ written raw and scroll / erase / resize move the image with its text.
147
+ - Without that feature the inline render returns the `[image/png …]` placeholder
148
+ instead. An image welded to the outer terminal's physical cursor for the rest
149
+ of the session is worse than no image.
150
+ - Absolutely positioned overlays (the pet) are unaffected: they carry their own
151
+ coordinates through the envelope and remain the only passthrough user.
152
+
153
+ ### Added (Sayknow Pet under tmux on kitty-protocol terminals)
154
+
155
+ Ghostty, Kitty and WezTerm implement kitty graphics and no sixel at all, so the
156
+ sixel probe alone left them with no pet inside tmux. SKC now also forwards a
157
+ kitty capability query (`a=q`) through the passthrough envelope. tmux cannot
158
+ answer it on the terminal's behalf — it does not implement the protocol — so an
159
+ `OK` coming back is genuine end-to-end evidence, unlike tmux's compile-time DA1
160
+ sixel claim.
161
+
162
+ - A successful query enables a dedicated **overlay** channel
163
+ (`setTmuxOverlayImageProtocol`) rather than inline image rendering: absolutely
164
+ positioned art can carry its own cursor addressing through the envelope, while
165
+ inline placements would land at tmux's stale physical cursor.
166
+ - Overlay payloads now carry the pane origin (`#{pane_top}`, `#{pane_left}`, and
167
+ top status lines), so a split window or a top status bar no longer draws the
168
+ pet in the wrong place. This also fixes the pre-existing sixel-fallback path.
169
+ - `allow-passthrough` is requested pane-locally at startup, so a tmux pane the
170
+ user created by hand works without manual configuration.
171
+ - Kill switch: `SKC_KITTY_MULTIPLEXER=0`.
172
+ - The tmux-specific unavailable warning no longer tells users to leave the
173
+ multiplexer or to force sixel; it names the actual requirement.
174
+
175
+ Verified end to end on Ghostty 1.3.1 + tmux 3.6b: pane passthrough is enabled
176
+ automatically, the probe reports `ImageProtocol.Kitty`, and the outer terminal's
177
+ real cell metrics (16x34) replace the 9x18 default.
178
+
8
179
  ## [0.5.1] — 2026-07-29
9
180
 
10
- Built on upstream **gajae-code v0.12.0**.
181
+ Built on upstream **v0.12.0**.
11
182
 
12
183
  ### Fixed (Sayknow Pet still left sixel residue under tmux)
13
184
 
@@ -32,7 +203,7 @@ exactly the people who already had a session open.
32
203
 
33
204
  ## [0.5.0] — 2026-07-28
34
205
 
35
- Built on upstream **gajae-code v0.12.0** (previous fork release tracked v0.11.6).
206
+ Built on upstream **v0.12.0** (previous fork release tracked v0.11.6).
36
207
 
37
208
  ### Security (inherited from upstream v0.12.0)
38
209
 
@@ -89,11 +260,11 @@ and that summary is injected into every later session.
89
260
 
90
261
  ### Fixed (rebrand leakage)
91
262
 
92
- - **Stop creating `.gjc-*` paths.** `recovery_fs` and `path_identity` still wrote
93
- `.gjc-recovery`, `.gjc-managed-remove-*`, and `.gjc-exact-unlink-*` on disk: the
94
- rename never reached them, so recovery state landed under an upstream-branded
95
- directory name. 24 residual tokens are gone and the sync pipeline now fails the
96
- build instead of shipping them.
263
+ - **Stop creating legacy-branded recovery paths.** `recovery_fs` and
264
+ `path_identity` still wrote legacy-branded recovery and removal marker
265
+ directories on disk: the rename never reached them, so recovery state landed
266
+ under an upstream-branded directory name. 24 residual tokens are gone and the
267
+ build now fails instead of shipping them.
97
268
 
98
269
  ### Fixed (fork pipeline)
99
270
 
@@ -257,7 +428,7 @@ platform subpackages publish from a single CI run.
257
428
 
258
429
  ### Changed
259
430
 
260
- - Synced onto upstream **gajae-code v0.11.6** (from v0.6.0), a 5-minor-version jump
431
+ - Synced onto upstream **v0.11.6** (from v0.6.0), a 5-minor-version jump
261
432
  bringing v0.7–v0.11 evolution: managed chat daemon (#2782, #2785, #2786), Telegram
262
433
  lock auto-reconciliation (#2781), compiled startup import-cycle fix (#2779), legacy
263
434
  daemon tombstone reclamation (#2780), nextest CI hardening (#2777), and the new
@@ -308,7 +479,7 @@ platform subpackages publish from a single CI run.
308
479
 
309
480
  ### Changed
310
481
 
311
- - Synced onto upstream **gajae-code v0.7.1** (from v0.6.5), bringing 0.7.0's mobile
482
+ - Synced onto upstream **v0.7.1** (from v0.6.5), bringing 0.7.0's mobile
312
483
  notifications SDK + managed Telegram daemon and 0.7.1's fixes (assistant
313
484
  notification lead-in, stale tmux session reuse, packaged native imports, and the
314
485
  glm-zcode Z.AI provider) while preserving the Sayknow-CLI brand.
@@ -348,7 +519,7 @@ platform subpackages publish from a single CI run.
348
519
 
349
520
  ### Changed
350
521
 
351
- - Synced onto upstream **gajae-code v0.6.5** (from v0.6.0), bringing its latest
522
+ - Synced onto upstream **v0.6.5** (from v0.6.0), bringing its latest
352
523
  features and fixes while preserving the Sayknow-CLI brand. The new welcome
353
524
  logo-mode support renders the blue **SAYKNOW** wordmark in every mode.
354
525
 
@@ -412,7 +583,7 @@ platform subpackages publish from a single CI run.
412
583
 
413
584
  ### Changed
414
585
 
415
- - Synced onto upstream **gajae-code v0.6.0** (52 commits, +27k lines), bringing its
586
+ - Synced onto upstream **v0.6.0** (52 commits, +27k lines), bringing its
416
587
  new features and fixes — including the opt-in `skc rlm` research mode, the goal
417
588
  `pause` operation, steer-by-default while busy, and the experimental
418
589
  desktop-control tool surface — while preserving the Sayknow-CLI brand and the
@@ -420,7 +591,7 @@ platform subpackages publish from a single CI run.
420
591
 
421
592
  ## [0.1.0] — 2026-06-17
422
593
 
423
- Initial Sayknow-CLI release, forked from gajae-code v0.5.4.
594
+ Initial Sayknow-CLI release, forked from upstream v0.5.4.
424
595
 
425
596
  ### Added
426
597
 
@@ -438,7 +609,7 @@ Initial Sayknow-CLI release, forked from gajae-code v0.5.4.
438
609
 
439
610
  ### Changed
440
611
 
441
- - Rebranded `gajae-code` → **Sayknow-CLI** (`skc`, `@sayknow-cli/*`) and reset the
612
+ - Rebranded the upstream harness → **Sayknow-CLI** (`skc`, `@sayknow-cli/*`) and reset the
442
613
  version to 0.1.0.
443
614
  - The model selector now starts OAuth login directly when you pick an
444
615
  unauthenticated preset, instead of only printing a hint.
@@ -56,10 +56,14 @@ type PathPlatform = NodeJS.Platform;
56
56
  type PackageExists = (packageName: string, packageRoot: string) => boolean;
57
57
  export declare function resolveUpdateMethodForTest(ompPath: string, bunBinDir: string | undefined): "bun" | "npm" | "binary";
58
58
  export declare function resolveNpmManagedTargetForTest(ompPath: string, platform: PathPlatform, packageExists: PackageExists): PackageManagerTarget | undefined;
59
+ export declare function getBinaryNameForTest(platform?: NodeJS.Platform, arch?: string): string;
60
+ export declare function fetchExpectedBinaryDigest(version: string, assetName: string, fetchImpl?: (url: string) => Promise<Response>): Promise<string>;
61
+ export declare function verifyReleaseBinaryIntegrity(filePath: string, version: string, assetName: string, fetchImpl?: (url: string) => Promise<Response>): Promise<void>;
59
62
  export declare function formatBinaryDownloadFailureMessageForTest(binaryName: string, url: string, status: string | number, platform?: NodeJS.Platform): string;
60
63
  export declare function buildReleaseBinaryUrlForTest(version: string, platform?: NodeJS.Platform, arch?: string): string;
61
64
  export declare function formatManualUpdateInstructionsForTest(platform?: NodeJS.Platform): string;
62
65
  export declare function formatVerificationFailureForTest(result: InstalledVersionVerification, expectedVersion: string): string;
66
+ export declare function withBinaryUpdateLock<T>(targetPath: string, operation: () => Promise<T>): Promise<T>;
63
67
  /**
64
68
  * Atomically replace the installed binary and roll back if version verification fails.
65
69
  */
@@ -69,16 +73,19 @@ export declare function fsyncFileForTest(filePath: string): Promise<void>;
69
73
  /** Injectable steps of the binary update flow (seams for testing ordering). */
70
74
  export interface BinaryUpdateFlow {
71
75
  download(url: string, tempPath: string): Promise<void>;
76
+ verifyIntegrity(filePath: string): Promise<void>;
72
77
  fsync(filePath: string): Promise<void>;
73
78
  replace(options: BinaryReplacementOptions): Promise<InstalledVersionVerification>;
74
79
  verifyInstalledVersion(expectedVersion: string): Promise<InstalledVersionVerification>;
75
80
  /** Best-effort cleanup of the temp file when the flow aborts before replace. */
76
81
  removeTemp?(filePath: string): Promise<void>;
82
+ /** Stable suffix for focused tests; production uses a process-unique staging path. */
83
+ transactionId?: string;
77
84
  /** Called once fsync has succeeded, right before replacement begins. */
78
85
  beforeReplace?(): void;
79
86
  }
80
87
  /**
81
- * Orchestrate download → fsync → replace → verify with a strict ordering
88
+ * Orchestrate download → integrity verification → fsync → replace → version/smoke verification.
82
89
  * contract: the downloaded temp binary MUST be flushed to stable storage
83
90
  * before it is published (renamed into place) or exec'd for verification.
84
91
  *
@@ -24,6 +24,13 @@ export default class Session extends Command {
24
24
  "state-file": import("@sayknow-cli/utils/cli").FlagDescriptor<"string"> & {
25
25
  description: string;
26
26
  };
27
+ "dry-run": import("@sayknow-cli/utils/cli").FlagDescriptor<"boolean"> & {
28
+ description: string;
29
+ default: boolean;
30
+ };
31
+ reference: import("@sayknow-cli/utils/cli").FlagDescriptor<"string"> & {
32
+ description: string;
33
+ };
27
34
  };
28
35
  static examples: string[];
29
36
  run(): Promise<void>;
@@ -72,6 +72,24 @@ export declare class AtomicYamlReplaceError extends Error {
72
72
  readonly code = "ATOMIC_YAML_REPLACE_FAILED";
73
73
  constructor(configPath: string, attempts: number, cause: unknown);
74
74
  }
75
+ /** Raised when the configured lexical route no longer resolves to its original target. */
76
+ export declare class AtomicYamlRetargetError extends Error {
77
+ readonly code = "ATOMIC_YAML_RETARGETED";
78
+ constructor();
79
+ }
80
+ /** Test-only coordination hooks for deterministic target-authority races. */
81
+ export declare const AtomicYamlTestHooks: {
82
+ afterTargetBound?: (canonicalPath: string) => Promise<void> | void;
83
+ };
84
+ type AtomicYamlTargetIdentity = {
85
+ device: bigint;
86
+ inode: bigint;
87
+ };
88
+ type AtomicYamlTarget = {
89
+ canonicalPath: string;
90
+ resolvedPath: string;
91
+ identity: AtomicYamlTargetIdentity | undefined;
92
+ };
75
93
  /** Set a dotted YAML path, creating object intermediates as needed. */
76
94
  export declare function setByPath(value: Record<string, unknown>, segments: readonly string[], nextValue: unknown): void;
77
95
  /** Delete a dotted YAML path without disturbing sibling keys or parent objects. */
@@ -85,7 +103,7 @@ export declare function applyAtomicYamlPatchesWithCurrent(configPath: string, bu
85
103
  * only when this operation reaches the front of the in-process queue, which lets
86
104
  * Settings debounce/coalesce inside its already-reserved causal slot.
87
105
  */
88
- export declare function enqueueAtomicYamlOperation<T>(configPath: string, operation: (canonicalConfigPath: string) => Promise<T>): Promise<T>;
106
+ export declare function enqueueAtomicYamlOperation<T>(configPath: string, operation: (canonicalConfigPath: string, target: AtomicYamlTarget) => Promise<T>): Promise<T>;
89
107
  /**
90
108
  * Reserve an atomic patch slot now, producing patches only after earlier slots
91
109
  * complete. Writers with state-aware merges use {@link reserveAtomicYamlUpdateSlot}.
@@ -101,3 +119,4 @@ export declare function reserveAtomicYamlUpdateSlot<T>(configPath: string, updat
101
119
  * cross-process file lock. Success means the temp file was fsynced and renamed.
102
120
  */
103
121
  export declare function applyAtomicYamlPatches(configPath: string, patches: readonly AtomicYamlPatch[], options?: AtomicYamlPatchOptions): Promise<CasReceipt>;
122
+ export {};
@@ -16,6 +16,7 @@ interface AppKeybindings {
16
16
  "app.model.select": true;
17
17
  "app.model.selectTemporary": true;
18
18
  "app.tools.expand": true;
19
+ "app.todo.toggle": true;
19
20
  "app.tool.backgroundFold": true;
20
21
  "app.editor.external": true;
21
22
  "app.message.followUp": true;
@@ -24,6 +25,7 @@ interface AppKeybindings {
24
25
  "app.clipboard.pasteImage": true;
25
26
  "app.clipboard.copyLine": true;
26
27
  "app.clipboard.copyPrompt": true;
28
+ "app.oauth.copyUrl": true;
27
29
  "app.session.new": true;
28
30
  "app.session.tree": true;
29
31
  "app.session.fork": true;
@@ -109,6 +111,10 @@ export declare const KEYBINDINGS: {
109
111
  readonly defaultKeys: "ctrl+o";
110
112
  readonly description: "Expand tools";
111
113
  };
114
+ readonly "app.todo.toggle": {
115
+ readonly defaultKeys: "alt+shift+t";
116
+ readonly description: "Toggle todo list expansion";
117
+ };
112
118
  readonly "app.tool.backgroundFold": {
113
119
  readonly defaultKeys: "ctrl+b";
114
120
  readonly description: "Fold/background supported foreground tool";
@@ -141,6 +147,10 @@ export declare const KEYBINDINGS: {
141
147
  readonly defaultKeys: "alt+shift+c";
142
148
  readonly description: "Copy prompt";
143
149
  };
150
+ readonly "app.oauth.copyUrl": {
151
+ readonly defaultKeys: "alt+shift+u";
152
+ readonly description: "Copy OAuth authorization URL";
153
+ };
144
154
  readonly "app.session.new": {
145
155
  readonly defaultKeys: "ctrl+n";
146
156
  readonly description: "Start a new session";
@@ -96,7 +96,7 @@ export declare const ModelsConfigFile: ConfigFile<{
96
96
  authHeader?: boolean | undefined;
97
97
  auth?: "none" | "apiKey" | "oauth" | undefined;
98
98
  discovery?: {
99
- type: "ollama" | "lm-studio" | "llama.cpp" | "openai-models-list";
99
+ type: "ollama" | "lm-studio" | "omlx" | "sglang" | "llama.cpp" | "openai-models-list";
100
100
  } | undefined;
101
101
  requestTransform?: {
102
102
  profile?: "openai-proxy" | undefined;
@@ -260,6 +260,8 @@ export declare const ProviderDiscoverySchema: z.ZodObject<{
260
260
  type: z.ZodEnum<{
261
261
  ollama: "ollama";
262
262
  "lm-studio": "lm-studio";
263
+ omlx: "omlx";
264
+ sglang: "sglang";
263
265
  "llama.cpp": "llama.cpp";
264
266
  "openai-models-list": "openai-models-list";
265
267
  }>;
@@ -368,6 +370,8 @@ export declare const ModelsConfigSchema: z.ZodObject<{
368
370
  type: z.ZodEnum<{
369
371
  ollama: "ollama";
370
372
  "lm-studio": "lm-studio";
373
+ omlx: "omlx";
374
+ sglang: "sglang";
371
375
  "llama.cpp": "llama.cpp";
372
376
  "openai-models-list": "openai-models-list";
373
377
  }>;
@@ -1663,6 +1663,15 @@ export declare const SETTINGS_SCHEMA: {
1663
1663
  }];
1664
1664
  };
1665
1665
  };
1666
+ readonly "startup.skipLogoAnimation": {
1667
+ readonly type: "boolean";
1668
+ readonly default: false;
1669
+ readonly ui: {
1670
+ readonly tab: "interaction";
1671
+ readonly label: "Skip Logo Animation";
1672
+ readonly description: "Show the final welcome logo immediately";
1673
+ };
1674
+ };
1666
1675
  readonly "startup.checkUpdate": {
1667
1676
  readonly type: "boolean";
1668
1677
  readonly default: true;
@@ -6,9 +6,17 @@
6
6
  * process, tracks its PID so repeated skc invocations don't double-spawn,
7
7
  * and redirects output to a log file under the config root.
8
8
  */
9
+ import { type Settings } from "./settings";
10
+ /**
11
+ * The startup command runs against an explicit Settings instance (tests and
12
+ * embedders inject one), so read through it instead of the global proxy —
13
+ * which may not be initialized at all on those paths.
14
+ */
15
+ type TelegramSettingsSource = Pick<Settings, "get">;
9
16
  /**
10
17
  * If `telegram.enabled` is true and the gateway is not already running, spawn
11
18
  * it in the background. Never throws — failures are logged as warnings so the
12
19
  * main skc session is unaffected.
13
20
  */
14
- export declare function maybeAutostartTelegramRemote(): Promise<void>;
21
+ export declare function maybeAutostartTelegramRemote(settings?: TelegramSettingsSource): Promise<void>;
22
+ export {};
@@ -35,5 +35,6 @@ export declare class DapClient {
35
35
  sendRequest<TBody = unknown>(command: string, args?: unknown, signal?: AbortSignal, timeoutMs?: number): Promise<TBody>;
36
36
  sendResponse(request: DapRequestMessage, success: boolean, body?: unknown, message?: string): Promise<void>;
37
37
  dispose(): Promise<void>;
38
+ startMessageReader(): Promise<void>;
38
39
  }
39
40
  export {};
@@ -5,6 +5,11 @@ import type { LspClient, ServerConfig } from "./types";
5
5
  */
6
6
  export declare function setIdleTimeout(ms: number | null | undefined): void;
7
7
  export declare function isIdleCheckerActiveForTests(): boolean;
8
+ /**
9
+ * Start background message reader for a client.
10
+ * Routes responses to pending requests and handles notifications.
11
+ */
12
+ export declare function startMessageReader(client: LspClient): Promise<void>;
8
13
  /** Timeout for warmup initialize requests (5 seconds) */
9
14
  export declare const WARMUP_TIMEOUT_MS = 5000;
10
15
  /**
@@ -9,15 +9,16 @@ export declare function isPetAvailable(): boolean;
9
9
  export declare function createPetSelectItems(options: ReadonlyArray<SelectItem>, currentValue: string, available: boolean): SelectItem[];
10
10
  /**
11
11
  * Grace period before declaring the terminal pet-incapable at startup. The
12
- * asynchronous Sixel capability probe starts inside `TUI.start()` and answers
13
- * within its own 250 ms deadline; this margin covers probe scheduling so a
14
- * supported terminal is never told it is incompatible while the probe is
15
- * still in flight.
12
+ * asynchronous capability probes start inside `TUI.start()` and answer within
13
+ * their own deadline (250 ms directly, 600 ms through tmux passthrough); this
14
+ * margin covers probe scheduling so a supported terminal is never told it is
15
+ * incompatible while a probe is still in flight.
16
16
  */
17
- export declare const PET_CAPABILITY_SETTLE_MS = 1000;
17
+ export declare const PET_CAPABILITY_SETTLE_MS = 1500;
18
18
  /**
19
- * Whether the asynchronous startup Sixel capability probe may still enable
20
- * graphics for this session, meaning current unavailability is not final.
19
+ * Whether an asynchronous startup capability probe (Sixel, or Kitty through
20
+ * tmux passthrough) may still enable graphics for this session, meaning current
21
+ * unavailability is not final.
21
22
  */
22
23
  export declare function isPetCapabilityProbePending(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
23
24
  /**
@@ -1,7 +1,7 @@
1
1
  import { Container, type SelectList } from "@sayknow-cli/tui";
2
2
  import type { PetMode } from "./sayknow-pet-widget";
3
3
  /**
4
- * Theme-style picker for the sayknow pet skin (Off / RedSayknow / BlueSayknow). Preview
4
+ * Theme-style picker for the sayknow pet skin (Off / RedOctopus / BlueOctopus). Preview
5
5
  * fires as the selection moves; select commits, cancel restores.
6
6
  */
7
7
  export declare class PetSelectorComponent extends Container {
@@ -50,7 +50,7 @@ export declare class SayknowPetWidget {
50
50
  setMode(mode: PetMode): void;
51
51
  /** Live preview during a selector: change the sprite without re-mounting the
52
52
  * composer editor (that would tear down the open overlay). After a short idle
53
- * eye-roll it fires the signature burst once (RedSayknow flex, BlueSayknow para-para
53
+ * eye-roll it fires the signature burst once (RedOctopus flex, BlueOctopus para-para
54
54
  * then sob) so the selector demos the animation instead of waiting the random gap. */
55
55
  previewMode(mode: PetMode): void;
56
56
  commitPreviewMode(mode: PetMode): void;
@@ -18,6 +18,7 @@ export interface WelcomeComponentOptions {
18
18
  collapseChangelog?: boolean;
19
19
  buildLabel?: string;
20
20
  keyDisplayContext?: KeyDisplayContext;
21
+ skipLogoAnimation?: boolean;
21
22
  }
22
23
  /**
23
24
  * Sayknow-CLI launch surface: a blue-gradient SAYKNOW wordmark, compact
@@ -110,7 +110,9 @@ export declare class SelectorController {
110
110
  showUserMessageSelector(): void;
111
111
  showTreeSelector(): void;
112
112
  showSessionSelector(): Promise<void>;
113
- handleResumeSession(sessionPath: string): Promise<void>;
113
+ handleResumeSession(sessionPath: string, options?: {
114
+ requireIdle?: boolean;
115
+ }, onSwitched?: () => void): Promise<boolean>;
114
116
  handleSessionDeleteCommand(): Promise<void>;
115
117
  showOAuthSelector(mode: "login" | "logout", providerId?: string, options?: OAuthSelectorOptions): Promise<void>;
116
118
  showDebugSelector(): void;
@@ -57,6 +57,12 @@ export interface InteractiveModeOptions {
57
57
  initialMessages?: string[];
58
58
  }
59
59
  export declare function selectShutdownDraft(editorText: string, hasActiveBtw: boolean): string;
60
+ export declare class OAuthAuthorizationUrlState {
61
+ #private;
62
+ set(url: string): number;
63
+ clear(lease: number): void;
64
+ get current(): string | undefined;
65
+ }
60
66
  export declare class InteractiveMode implements InteractiveModeContext {
61
67
  #private;
62
68
  session: AgentSession;
@@ -243,6 +249,10 @@ export declare class InteractiveMode implements InteractiveModeContext {
243
249
  handleContributionPrepCommand(customInstructions?: string): Promise<void>;
244
250
  executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise<CompactionOutcome>;
245
251
  openInBrowser(urlOrPath: string): void;
252
+ setOAuthAuthorizationUrl(url: string): number;
253
+ clearOAuthAuthorizationUrl(lease: number): void;
254
+ hasOAuthAuthorizationUrl(): boolean;
255
+ copyOAuthAuthorizationUrl(): Promise<void>;
246
256
  showCommandPalette(commands: SlashCommand[], actions: CommandPaletteAction[], executeSlashCommand: (name: string) => Promise<void>): void;
247
257
  showSettingsSelector(): void;
248
258
  showThemeSelector(): void;
@@ -259,7 +269,9 @@ export declare class InteractiveMode implements InteractiveModeContext {
259
269
  showUserMessageSelector(): void;
260
270
  showTreeSelector(): void;
261
271
  showSessionSelector(): void;
262
- handleResumeSession(sessionPath: string): Promise<void>;
272
+ handleResumeSession(sessionPath: string, options?: {
273
+ requireIdle?: boolean;
274
+ }): Promise<boolean>;
263
275
  handleSessionDeleteCommand(): Promise<void>;
264
276
  showOAuthSelector(mode: "login" | "logout", providerId?: string, options?: import("./types").OAuthSelectorOptions): Promise<void>;
265
277
  showHookConfirm(title: string, message: string): Promise<boolean>;
@@ -67,6 +67,12 @@ export declare function modelSupportsTokenCostMetrics(model: Model | undefined):
67
67
  export interface WorkflowGateEmitter {
68
68
  /** True only when unattended mode has been negotiated. */
69
69
  isUnattended(): boolean;
70
+ /**
71
+ * True when gates emitted through this emitter can be answered remotely
72
+ * (workflow_gate_response over RPC/bridge). The ask tool refuses headless
73
+ * execution unless this reports true.
74
+ */
75
+ supportsRemoteGateAnswers(): boolean;
70
76
  /** Open + emit a gate; resolves with the agent's answer (from workflow_gate_response). */
71
77
  emitGate(input: OpenGateInput): Promise<unknown>;
72
78
  /**
@@ -107,6 +113,7 @@ export declare class UnattendedSessionControlPlane implements RpcUnattendedContr
107
113
  private readonly opts;
108
114
  constructor(opts: UnattendedSessionOptions);
109
115
  isUnattended(): boolean;
116
+ supportsRemoteGateAnswers(): boolean;
110
117
  /** Observe every emitted gate (e.g. so an extension can map an ask to its gate_id). */
111
118
  onGateEmitted(listener: (gate: RpcWorkflowGate) => void): () => void;
112
119
  get controller(): UnattendedRunController | undefined;
@@ -60,6 +60,8 @@ export interface AskSelectedAckRecoveryParticipant {
60
60
  /** SDK-native surface for emitting a workflow gate and awaiting its answer. */
61
61
  export interface WorkflowGateEmitter {
62
62
  supportsRemoteGateAnswers(): boolean;
63
+ /** True when an unattended run has been negotiated for this emitter (control-plane emitters). */
64
+ isUnattended?(): boolean;
63
65
  emitGate(input: OpenGateInput): Promise<unknown>;
64
66
  onGateEmitted?(listener: (gate: WorkflowGate) => void): () => void;
65
67
  resolveGate?(response: WorkflowGateResponse): Promise<WorkflowGateResolution>;
@@ -261,6 +261,10 @@ export interface InteractiveModeContext {
261
261
  handleSTTToggle(): Promise<void>;
262
262
  executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise<CompactionOutcome>;
263
263
  openInBrowser(urlOrPath: string): void;
264
+ setOAuthAuthorizationUrl(url: string): number;
265
+ hasOAuthAuthorizationUrl(): boolean;
266
+ clearOAuthAuthorizationUrl(lease: number): void;
267
+ copyOAuthAuthorizationUrl(): Promise<void>;
264
268
  /** Resolved source of truth for slash autocomplete and command palette entries. */
265
269
  getSlashCommands?(): readonly SlashCommand[];
266
270
  refreshSlashCommandState(cwd?: string): Promise<void>;
@@ -281,7 +285,9 @@ export interface InteractiveModeContext {
281
285
  showTreeSelector(): void;
282
286
  showSessionSelector(): void;
283
287
  showSessionsDashboard(): void;
284
- handleResumeSession(sessionPath: string): Promise<void>;
288
+ handleResumeSession(sessionPath: string, options?: {
289
+ requireIdle?: boolean;
290
+ }): Promise<boolean>;
285
291
  handleSessionDeleteCommand(): Promise<void>;
286
292
  showOAuthSelector(mode: "login" | "logout", providerId?: string, options?: OAuthSelectorOptions): Promise<void>;
287
293
  showHookConfirm(title: string, message: string): Promise<boolean>;
@@ -5,6 +5,8 @@
5
5
  * Messages are newline-delimited JSON.
6
6
  */
7
7
  import type { MCPRequestOptions, MCPStdioServerConfig, MCPTransport } from "../../runtime-mcp/types";
8
+ /** Write one JSONL frame while honoring Bun FileSink backpressure and flush failures. */
9
+ export declare function writeStdioFrame(stdin: Bun.FileSink, message: string): Promise<void>;
8
10
  export declare class StdioTransport implements MCPTransport {
9
11
  #private;
10
12
  private config;